home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Developer Helper 1: Phil & Dave's Excellent CD
/
Excellent CD HFS.raw
/
Moof
/
Goodies
/
HyperCard Goodies
/
AppleTalk Stuff
/
HyperAppleTalk
/
MISC
/
atalkXCMD.c
< prev
next >
Wrap
Text File
|
1988-05-12
|
38KB
|
1,346 lines
/*******************************************************************\
* file: atalkXCMD.c *
* version: 1.06ß *
* *
* *
* The following XCMD's give you AppleTalk capability (at the the *
* transaction and name-binding protocol layers) to Hypercard. While *
* not an exhaustive treatment, it's a start. *
* *
* code known to work with correctly with the prototype as a client *
* and/or server *
* ----------------------------------------------------------------- *
* Routine | Description *
* ----------------------------------------------------------------- *
* *
* --- AppleTalk Transaction Protocol (ATP) Routines --- *
* ATPInit Check to see if port available and appletalk drivers*
* loaded, allocate required buffers. Open a server *
* socket for this node. *
* ATPKill Disassociate ourselves with the net deallocate *
* buffers *
* PollRequests Poll the Idle queue and return info *
* Request Determine whether a request can be sent or needs to *
* be added to the waiting queue. *
* entity. *
* *
* --- Name Binding Protocol (NBP) Routines --- *
* NBPInit set up the NBP stuff *
* NBPKill close down the NBP stuff *
* ConfirmName confirm that an entity is still visible (should be *
* done before sending a message to that entity) *
* ExtractName Extract a name from the Lookup buffer *
* Lookup Look up other entities on the net and put the *
* results in LookUpBuffer. *
* NodeRegister Get the name of this node and register it in the *
* socket listening table. *
* *
* --- Miscellaneous Support Routines --- *
* DeQueueRequest *
* Remove an element from waiting queue (FIFO style) *
* QueueGetRequest *
* Queue a GetRequest on the server side to "listen" *
* for incoming client requests. *
* QueueRequest Queue a request that cannot be services due to lack *
* of sockets. Copy all the data into queue structure *
* Respond Respond to a request with the given message *
* SendRequest Set up an ATP parameter block and send the request *
* SendWaitingRequest *
* Attempt to send a queued request. If successful, *
* dequeue it, otherwise leave it in the queue for *
* another try later. *
* ----------------------------------------------------------------- *
* By: Donald Koscheka *
* with Gratitudes to Kerry Lynn for debugging help *
* and Platitudes to Greg Kimberly for thinking up the idea *
* Date: 21-Sept-87 *
* © Copyright 1987, Apple Computer, Inc. *
* All Rights Reserved *
* *
* ----------------------------------------------------------------- *
* Modification History *
* ----------------------------------------------------------------- *
* Date | By | Description *
* ----------------------------------------------------------------- *
* 9/21/87 | DK | file created *
* 10/1/87 | DK | modified to work with preferred ATP calls *
* 10/5/87 | DK | relocate the master block at heap high *
* 10/6/87 | DK | added dynamically allocated lookup buffer *
* 10/7/87 | DK | separated response from Poll Request *
* 10/7/87 | DK | extract responses during Poll_Requests *
* 11/5/87 | DK | modified all routines to return the error *
* | DK | removed paramBlock from all calls not making *
* | | callbacks to hypercard. *
* | | modified code to reflect changes in 11/5 spec *
* ----------------------------------------------------------------- *
* Alpha Release 0.5 11/30/87 *
* ----------------------------------------------------------------- *
* 12/1/87 | DK | Modified receive to put received data in *
* | | global pool and call back with method only *
* 12/2/87 | DK | Modified waiting queue to act as a FIFO *
* | DK | issue responses asynchronously. *
* 12/3/87 | DK | Added full response packet transmission *
* 12/7/87 | DK | Converted NBP calls to preferred (paramblock) *
* 12/10/87 | DK | General bug fixes - check handles... *
* 12/11/87 | DK | converted nonrels from locked handles to Ptrs *
* ----------------------------------------------------------------- *
* Beta Release 1.05ß 12/15/87 *
* ----------------------------------------------------------------- *
* 1/14/88 | DK | Converted all routines to access names table *
* | | Uncouple NBP and ATP stuff *
* 1/26/88 | DK | set SPConfigP to 0x01 to indicate Appletalk is*
* | | is using port B *
* 2/10/88 | DK | Set atp.checkpoint flag to signal that we're *
* | | currently in ATPReceive (ATPClose can't close)*
* | | or ATPClose sets to tell ATPReceive to close *
* 22-Feb-88 | DK | Move all locked handles high *
* 29-Feb-88 | DK | Dispose handle created for response callback *
* ----------------------------------------------------------------- *
\*******************************************************************/
/*******************************************************************\
* HUH???? Experienced "C" programmers will note the capricious *
* use of the commented "auto" storage class. I do this so that *
* my data declarations "line up" as: CLASS TYPE NAME = INITIALIZER *
* This pedantry is provided as an aid to the beginner. Also, *
* remembering that automatics are stack-bound might help the novice *
* look for them in a stack frame from the debugger(Hint: A6 is the *
* frame pointer). *
* -DK *
\*******************************************************************/
#include <Types.h>
#include <AppleTalk.h>
#include <Resources.h>
#include <OSUtils.h>
#include <Memory.h>
#include <Strings.h>
#include "atalkxcmd.h"
#include "xcmdutils.h"
#include "HyperXCmd.h"
/*******************************************************************\
* AppleTalk Transaction Protocol (ATP) Interface Routines *
* *
\*******************************************************************/
ATPBlock *ATPInit( entcount, type )
short entcount;
short type;
/*********************************
* Check to see if PortB is in use by
* a serial driver or wheter it is
* available to us for appletalk. If
* it is available, check PortBUse to
* determine whether appletalk is in.
*
* Allocate our buffers and open a
* listening socket for this node
* (talkers are allocated dynamically).
* queue up enough requests to get
* us going...
*
* In: short = number of entitities
* to allocate
*
* Out: Pointer to the allocated ATPBlock
* nil of the session was already initialized
*
* NOTE: We attempt to allocate (3)
* nonrels - ATPBlock, ServerBlock
* and ClientBlock. These objects
* MUST NOT move during the session
* (async I/O is depending on it)
*********************************/
{
/* auto */ char *PortBUseP;
/* auto */ char *SPConfigP;
/* auto */ char use, con;
/* auto */ char *temp;
/* auto */ OSErr error;
/* auto */ short i;
/* auto */ ATPParamBlock pb;
register ATPBlock *mbPtr;
/* point to and get the needed lowmem stuff. */
PortBUseP = (char *)PortBUse;
SPConfigP = (char *)SPConfig;
use = *PortBUseP;
con = *SPConfigP;
if((con & 0x0F) > useATalk) /** if the port is in use we can't usurp it **/
return( nil );
if( (use & 0xFF || use & 0x0F) == useATalk){
if(!IsMPPOpen())
if((error = MPPOpen()) != noErr)
return( nil );
if( !IsATPOpen() ){
if (error = ATPLoad() != noErr)
return( nil );
*PortBUseP |= 0x04;
*PortBUseP &= 0x7F;
}
}
else
return( nil );
*SPConfigP |= 0x01; /*** set for appletalk ***/
/*** attempt to allocate the data ***/
if( !(mbPtr = (Ptr)NewPtr( sizeof( ATPBlock ))) )
return( nil );
/*** ------ Initialize the Master Block ------ ***/
mbPtr->Server = nil;
mbPtr->Client = nil;
mbPtr->FirstRequest = nil; /*** no requests queued ***/
mbPtr->LastRequest = nil;
mbPtr->checkPoint = CLOSE_OK; /**** 2/10/88 ***/
temp = mbPtr->autoRsp;
*temp++ = 'O'; /*** if you receive this message ***/
*temp++ = 'K'; /*** from a callback, you know the ***/
*temp++ = 'A'; /*** transaction succeeded. ***/
*temp++ = 'Y'; /*** Isn't that special? ***/
*temp = '\0';
/*** ------ Initialize the server side ------ ***/
mbPtr->ServerAddr.aSocket = 0;
mbPtr->ServerAddr.aNode = 0;
mbPtr->ServerAddr.aNet = 0;
if( type && SERVER ){
pb.ATPaddrBlock.aNet = (short)0;
pb.ATPaddrBlock.aNode = 0;
pb.ATPaddrBlock.aSocket = 0;
pb.ATPatpSocket = 0;
if( !POpenATPSkt( &pb , SYNC ) ) /*** noErr means we have a socket***/
mbPtr->ServerAddr.aSocket = (unsigned char)pb.ATPatpSocket;
else{
ShutMeDown( mbPtr );
return( nil );
}
/*** initialize getrequests array ***/
{
register ServerPtr sp = mbPtr->Server;
mbPtr->Server = (ServerPtr)NewPtr( sizeof( ServerBlock )*SQSIZE );
if( mbPtr->Server )
sp = mbPtr->Server;
else{
ShutMeDown( mbPtr );
return( nil );
}
for( i=0; i < SQSIZE; i++ )
QueueGetRequest( &(sp[i]), &(mbPtr->ServerAddr) );
}
}
/*** ------ Initialize the client side ------ ***/
if( type && CLIENT ){
/*** initialize the send request queue ***/
{
ClientPtr cp;
mbPtr->Client = (ClientPtr)NewPtr( sizeof( ClientBlock ) * CQSIZE );
if( mbPtr->Client )
cp = mbPtr->Client;
else{
ShutMeDown( mbPtr );
return( nil );
}
for( i=0; i < CQSIZE; i++ ){
cp[i].isInUse = IDLE;
cp[i].rspM = nil;
}
}
}
return( mbPtr );
}
ShutMeDown( atp )
ATPBlock *atp;
/****************************
* Given a handle to an allocated
* ATPBlock, deallocate all its
* substructures and itself
*
* called only after a memory failure
* and atp already allocated.
****************************/
{
if( atp->Server )
DisposPtr( atp->Server );
if( atp->Client )
DisposPtr( atp->Client );
DisposPtr( atp );
}
short ATPKill( atp )
ATPBlock *atp;
/*********************************
* Remove ourselves from the entity
* table, dislocate our socket and
* deallocate all memory whose handles
* were provided...
*
* In: Handle to ATPBlock
*
* Out: noErr if successful
* OSErr otherwise
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ OSErr err;
/* auto */ short i;
/* auto */ ATPParamBlock pb;
/*** Close Down and Deallocate the SERVER side ***/
if( atp->Server ){
register ServerPtr sp = atp->Server;
if( atp->ServerAddr.aSocket ){
/*** close down any outstanding get requests***/
for( i = 0; i < SQSIZE; i++ )
if( sp[i].isInUse && sp[i].pb.ATPioResult > 0 ){
pb.ATPaKillQEl = &(sp[i].pb);
err = PKillGetReq( &pb, SYNC );
if( err )
error = err;
}
/*** shut down the server socket ***/
pb.ATPatpSocket = atp->ServerAddr.aSocket;
err = PCloseATPSkt( &pb, SYNC );
if( err )
error = err;
}
}
/*** Close Down and Deallocate CLIENT side ***/
if( atp->Client ){
register ClientPtr cp = atp->Client;
/*** Kill outstanding SendRequests ***/
for( i = 0; i < CQSIZE; i++ )
if( cp[i].isInUse && cp[i].pb.ATPioResult > 0 ){
pb.ATPaKillQEl = &(cp[i].pb);
err = PKillSendReq( &pb, SYNC );
if( err )
error = err;
if( cp[i].rspM ){
HUnlock( cp[i].rspM );
DisposHandle( cp[i].rspM );
}
}
/*** deallocate all waiting queue elements ***/
if( atp->FirstRequest ){
/* auto */ ClientQH nq, cq;
register ClientQPtr pq;
cq = atp->FirstRequest;
while( cq ){
nq = (**cq).next;
pq = *cq;
if( pq->rsp ){
HUnlock( pq->rsp );
DisposHandle( pq->rsp );
}
if( pq->buf ){
HUnlock( pq->buf );
DisposHandle( pq->buf );
}
HUnlock( cq );
DisposHandle( cq );
cq = nq;
}
}
}
ShutMeDown( atp );
return( error );
}
short PollRequests( paramPtr, atp, rspMess )
XCmdBlockPtr paramPtr;
ATPBlock *atp;
Handle rspMess;
/*********************************
* PollRequests
*
* Performs 3 functions:
*
* (1) Handle Received Requests on Server Side
*
* (2) Handle Succesful termination of Responses
*
* (3) Handle Received Responses on Client Side
*
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ short i;
if( atp->Server ){
error = Get_Requests_From_Clients( paramPtr, atp, rspMess );
error = Check_Response_Sent( paramPtr, atp );
}
if( atp->Client )
error = Get_Responses_From_Servers( paramPtr, atp );
if( error > 0 )
error = 0;
return( error );
}
short Request( atp, theData, theSize, addr, rmess, cnt, intv )
ATPBlock *atp;
unsigned char *theData;
short theSize;
AddrBlock *addr;
Handle rmess;
short cnt, intv;
/*********************************
* If a transaction block is available,
* send the request on to ATP. Otherwise,
* add it to the waiting queue.
*
* In: theData == the data to use
* theSize == the size of the data
* rmess == handle to the message to
* append the response to
* addr == tuple of the receiver
* retry, timeout
*
* Out: noErr if successful
* OSErr otherwise
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ short i;
register ClientPtr cp = atp->Client;
if( !atp->Client ) /*** take a powder you chowder head ***/
return( DEFAULT_ERROR );
/*** find the next available client transaction block (if any) ***/
for( i = 0; i < CQSIZE; i++ )
if( cp[i].isInUse == IDLE ){
error = SendRequest( &(cp[i]),theData, theSize, rmess, addr, cnt, intv );
return( error );
}
/*** didn't have an available transaction block, or the request failed to ***/
/*** to get satisfied (probably no available sockets), so pend the request ***/
QueueRequest( atp, theData, theSize, rmess, addr, cnt, intv );
return( noErr ); /*** queued requests don't fail ***/
}
/*******************************************************************\
* PollRequests Server/Client Routines *
\*******************************************************************/
Get_Requests_From_Clients( paramPtr, atp, rspMess )
XCmdBlockPtr paramPtr;
ATPBlock *atp;
char **rspMess;
/*********************************
* Get_Requests_From_Clients
*
* For all elements in the getRequest queue:
* if a getRequest is completed:
* - get the request data and put it into the global receive container
* - call hypercard back with the getrequest message
* - the responding method will put the response in "GlobalRSPData"
* - if the response message is nil, do an auto respond
* else send the response in the global container "GlobalRSPData"
* - deallocate the request and requeue a fresh one
*
* In: paramPtr = used for callbacks
* atp = ATPBlock pointer
* rspMess = message to send back to
* hypercard with Server-side getrequests.
*
* Out: calls back hypercard with the request
* error result returned.
*********************************/
{
/* auto */ short i;
/* auto */ OSErr error = noErr;
/* auto */ long rs;
/* auto */ Handle name, respData;
/* auto */ long respSize;
register ServerPtr sp;
if( rspMess )
c2pstr( *rspMess );
sp = atp->Server;
for( i = 0; i < SQSIZE ; i++ ){
error = sp[i].pb.ATPioResult; /*** didn't take***/
if( sp[i].isInUse == WAIT_REQUEST ){
if( error < 0 )
QueueGetRequest( &(sp[i]), &(atp->ServerAddr) );/*** try again ***/
else
if( !error ){
/*** put the request in a Hypercard global ***/
name = GetResource('STR ', GlobalRcvData );
respSize = (long)sp[i].pb.ATPreqLength;
respData = NewHandle( respSize );
BlockMove( sp[i].pb.ATPreqPointer, *respData, respSize );
MoveHHi( name );
HLock(name);
SetGlobal( paramPtr, *name, respData );
HUnlock(name);
DisposHandle( respData ); /* 2/29/88 */
/*** now call the responding method (if any) ***/
if( rspMess ){
MoveHHi( name );
HLock( rspMess );
SendCardMessage( paramPtr, *rspMess );
HUnlock( rspMess );
}
/*** if the responding method has a response to send ***/
/*** then send it, otherwise "autorespond" using the ***/
/*** canned response "OKAY" ***/
name = GetResource('STR ',GlobalRspData );
MoveHHi( name );
HLock(name);
respData = GetGlobal(paramPtr,*name);
HUnlock(name);
if ( respData && **respData ){
rs = GetHandleSize( respData );
MoveHHi( respData );
HLock( respData );
Respond( &(sp[i]), &(atp->ServerAddr), *respData, (short)rs );
HUnlock( respData );
}
else /*** send the autoresponse ***/
Respond( &(sp[i]),&(atp->ServerAddr),atp->autoRsp,(short)AUTOSIZE);
if( respData )
DisposHandle( respData ); /* 2/29/88 */
}
}
}/*** for loop ***/
}
Check_Response_Sent(paramPtr, atp )
XCmdBlockPtr paramPtr;
ATPBlock *atp;
/*********************************
* Send_Responses_To_Clients
*
* A server transaction block is considered
* in use from the time that the get request
* is queued unitl the TREL is received from
* the client (acknowledging receipt of the
* response).
*
* Actually, all this routine does is wait
* for the sent response to complete and
* requeue a get request when it does.
*
* In: paramPtr = used for callbacks
* atp = ATPBlock pointer
*
*********************************/
{
/* auto */ short i;
/* auto */ OSErr error = noErr;
register ServerPtr sp;
/*** See if any Responses have completed ***/
sp = atp->Server;
for( i = 0; i < SQSIZE; i++ )
if( sp[i].isInUse == WAIT_RESPONSE )
/*** waiting for the client to acknowledge receipt ***/
if( sp[i].pb.ATPioResult <= 0 ){
error = sp[i].pb.ATPioResult;
sp[i].isInUse == IDLE;
QueueGetRequest( &(sp[i]), &(atp->ServerAddr) );
}
}
Get_Responses_From_Servers(paramPtr, atp )
XCmdBlockPtr paramPtr;
ATPBlock *atp;
/*********************************
* Get_Responses_From_Servers
*
* For all elements in the request queue:
* if a sndRequest completes:
* - get the responding data and put it into the global receive containter
* - if the request contained a response message, call hypercard back
* with the message.
* - dequeue this request
*
* In: paramPtr = used for callbacks
* atp = ATPBlock pointer
*
* Out: calls back hypercard with the request
* error result returned.
*********************************/
{
/* auto */ short i,j;
/* auto */ OSErr error = noErr;
/* auto */ ClientPtr cp;
/* auto */ Handle name, RcvData;
register char *respData;
register short respSize;
cp = atp->Client;
for( i = 0; i < CQSIZE; i++ )
if( cp[i].isInUse == IDLE )
SendWaitingRequest( atp, &(cp[i]) );
else{
if( cp[i].pb.ATPioResult < 0 ){
/*** next user of the block will clean up the dangling data ***/
cp[i].isInUse = IDLE;
error = cp[i].pb.ATPioResult;
if( cp->rspM ){
HUnlock( cp->rspM );
DisposHandle( cp->rspM );
cp->rspM = nil;
}
}
if( cp[i].pb.ATPioResult == 0 ){
/*** got an answer from the server... ***/
/*** ***/
/*** To calculate the size, add up the sizes of each BDS ***/
/*** element. The buffers are contiguous so we don't have ***/
/*** to concatentate them to create the response message ***/
cp[i].isInUse = IDLE;
respData = (cp[i]).bds[0].buffPtr;
respSize = 0;
for( j = 0; j < (short)cp[i].pb.ATPnumOfResps; j++ )
respSize += cp[i].bds[j].dataSize;
/*** put the responding data in global pool and call back ***/
/*** hypercard with the response message ***/
name = (StringPtr *)GetResource('STR ', GlobalRcvData );
RcvData = NewHandle( (long)respSize );
if( RcvData ){
BlockMove( respData, *RcvData, (long)respSize );
MoveHHi( name );
HLock(name);
SetGlobal(paramPtr, *name, RcvData );
HUnlock(name);
DisposHandle( RcvData ); /* 2/29/88 */
}
/*** If response message was given, call back hypercard with ***/
/*** the message (handler finds its data in GlobalRcvData) ***/
if( cp[i].rspM ){
MoveHHi( cp[i].rspM );
HLock( cp[i].rspM );
c2pstr( *(cp[i].rspM) );
SendCardMessage( paramPtr, *(cp[i].rspM) );
HUnlock( cp[i].rspM );
DisposHandle( cp[i].rspM );
cp[i].rspM = nil;
}
}
}
return( error );
}
/*******************************************************************\
* (ATP) Interface Support Routines *
* *
* These routines are used in support of the ATP Interface routines *
* and are not called directly by the XCMD routines. *
\*******************************************************************/
DeQueueRequest( atp )
ATPBlock *atp;
/*********************************
* Remove an element from the waiting
* queue and update the linked list
* accordingly.
*
* Note: this is the only place an element can
* be removed from the queue. We use a FIFO
* method so that tq is ALWAYS pointing to
* the first element in the list!
*
* In: tq = handle to the element
* to dequeue
*********************************/
{
register ClientQH tq = atp->FirstRequest;
register ClientQPtr pq; /*** pointer for efficiency ***/
if( !tq ){
atp->FirstRequest = atp->LastRequest = nil;
return; /*** No queue element here ***/
}
pq = *tq;
/*** next becomes first ***/
if( pq->next ) /*** if there is one ***/
atp->FirstRequest = pq->next;
else /*** ...else the list is empty ***/
atp->FirstRequest = atp->LastRequest = nil;
if( pq->buf ){
HUnlock( pq->buf ); /*** Delete the element's data ***/
DisposHandle( pq->buf );
}
if( pq->rsp ){
HUnlock( pq->rsp );
DisposHandle( pq->rsp );
}
HUnlock( tq );
DisposHandle( tq );
}
QueueGetRequest( sp, srcaddr )
ServerPtr sp;
AddrBlock *srcaddr;
/*********************************
* requeue the get request element
* whose index is passed as a param.
*
* Note: This is a server side
* function only. You may
* have more than one getrequest
* "listening" on the server socket.
*
* if the request if not completed,
* do nothing.
*
* In: sp == pointer to request array element
* srcaddr == source address of this node
*
* returns nil if the request was queued
* noErr otherwise
*********************************/
{
/*auto */ short error = DEFAULT_ERROR;
register atpPBptr pb = &(sp->pb);
pb->ATPioCompletion = nil;
pb->ATPatpSocket = srcaddr->aSocket;
pb->ATPreqLength = (short)ATPBSIZE;
pb->ATPreqPointer = sp->buf;
sp->isInUse = WAIT_REQUEST; /*** set for the sake of Kill ***/
if( (error = PGetRequest( pb , ASYNC )) >= 0 )
error = noErr;
return( error );
}
QueueRequest( atp, theData, theSize, rmess, addr, cnt, intv )
ATPBlock *atp;
unsigned char *theData;
short theSize;
Handle rmess;
AddrBlock *addr;
short cnt, intv;
/*********************************
* Add an element to the waiting
* queue and update the linked list
* accordingly.
*
* Note: this is the only place an element can
* be added to the queue.
*
* In: theData == the data to use
* theSize == the size of the data
* rmess == handle to the message to
* append the response to
* addr == tuple of the receiver
* retry, timeout
*********************************/
{
/* auto */ long respSize;
/* auto */ ClientQH qelem;
register ClientQPtr qp; /*** ptr to client queue element***/
if( (qelem = NewHandle( sizeof( ClientQRec ))) == nil )
return( DEFAULT_ERROR );
/*** copy the pending data into the qelement ***/
MoveHHi( qelem );
HLock( qelem );
qp = *qelem;
qp->next = nil;
qp->interval = intv;
qp->count = cnt;
qp->addr.aNet = addr->aNet;
qp->addr.aNode = addr->aNode;
qp->addr.aSocket= addr->aSocket;
qp->size = (long)theSize;
qp->rsp = nil;
qp->buf = NewHandle( qp->size );
if( qp->buf )
BlockMove( theData, *(qp->buf), qp->size );
else{
HUnlock( qelem );
DisposHandle( qelem );
return( MEM_ERROR );
}
if( rmess ){ /*** if we have a handle and it's not empty ***/
respSize = GetHandleSize( rmess );
qp->rsp = NewHandle( respSize );
if( qp->rsp )
BlockMove( *rmess, *(qp->rsp), respSize );
else{
HUnlock( qp->buf );
DisposHandle( qp->buf );
HUnlock( qelem );
DisposHandle( qelem );
return( MEM_ERROR );
}
}
/*** Only after we're sure we put the request together do we add it to ***/
/*** the queue. ***/
if( !atp->FirstRequest )
atp->FirstRequest = atp->LastRequest = qelem;
else{ /*** add to end of list ***/
qp = *(atp->LastRequest);
qp->next = qelem;
atp->LastRequest = qelem;
}
HUnlock( qelem );
}
short Respond( sp, srcAddr, theData, theSize )
ServerPtr sp;
AddrBlock *srcAddr;
char *theData;
short theSize;
/*********************************
* Send a response to a requesting
* socket. This is a server side
* function only.
*
* This routine reuses the parameter
* block that was used by the getrequest.
* Because of this, the destination address
* and the transaction ID will already be
* valid.
*
* In: sp == pointer to the server block to use
* srcAddr == this server's address
* theData == the data to use
* theSize == the size of the data
*
* Out: noErr if successful
* OSErr otherwise
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ short bdsSize, i, bdsCount, bdsBufSiz;
register char *bdsPtr;
register atpPBptr pb = &(sp->pb);
/*** Note: because we are re-using the getrequest parameter block, the ***/
/*** transaction ID and the destination address are already set in the ***/
/*** parameter block. ***/
sp->isInUse = WAIT_RESPONSE;
pb->ATPioCompletion = nil;
pb->ATPatpSocket = srcAddr->aSocket;
pb->ATPatpFlags = atpEOMvalue;
pb->ATPbdsPointer = sp->rspBDS;
/*** set up the response BDS for this transactioncopy the data into the ***/
/*** rspbuffer (set last byte to zero ) ***/
bdsSize = ( theSize < (ATPBSIZE * MAXBDS) )? theSize : (ATPBSIZE * MAXBDS);
BlockMove( theData, sp->rspBuf, (long)bdsSize );
(sp->rspBuf)[bdsSize] = '\0'; /*** force to a "C" string ***/
/*** Set up response bds by pointing into the data at ATPBSIZE intervals ***/
bdsPtr = sp->rspBuf;
bdsCount = ( bdsSize + ATPBSIZE ) / (short)ATPBSIZE; /*** # bds elements ***/
for( i = 0; i < bdsCount; i++ ){
sp->rspBDS[i].buffPtr = bdsPtr;
bdsBufSiz = ( bdsSize < ATPBSIZE)? bdsSize : ATPBSIZE;
sp->rspBDS[i].buffSize = bdsBufSiz;
bdsSize -= ATPBSIZE;
if( bdsSize < 0 )
bdsSize = 0;
bdsPtr += ATPBSIZE;
/*** fix up the BDS to make it pretty for the scope ***/
sp->rspBDS[i].userBytes = (long)'BDS0';
sp->rspBDS[i].userBytes += (long)i;
}
pb->ATPbdsSize = (Byte)bdsCount; /*** total # of bds elems ***/
pb->ATPnumOfBuffs = (Byte)bdsCount; /*** number being sent now ***/
PSendResponse( pb, ASYNC ); /*** will catch the error on idle ***/
return( error );
}
SendRequest( cp, theData, theSize, rmess, addr, count, interval )
ClientPtr cp;
unsigned char *theData;
short theSize;
Handle rmess;
AddrBlock *addr;
short count, interval;
/*********************************
* Sends the request using the transaction
* block pointed to by cp. This is a client
* side function only.
*
* In: cp == pointer to transaction block
* theData == the data to use
* theSize == the size of the data
* rmess == the method to send back with the response
* addr == tuple of the receiver
* retry, timeout
*
* Out: noErr if successful
* OSErr otherwise
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ long rsize;
/* auto */ short i;
/* auto */ char *bdsPtr;
register atpPBptr pb = &(cp->pb);
cp->rspM = nil;
/*** copy the message over ***/
if( rmess ){
rsize = GetHandleSize( rmess );
cp->rspM = NewHandle( rsize );
if( !cp->rspM )
return( DEFAULT_ERROR );
else
BlockMove( *rmess, *(cp->rspM), rsize );
}
/*** set up the parameter block ***/
pb->ATPioCompletion = nil;
pb->ATPuserData = (long)0x52455153; /*** REQS ***/
pb->ATPatpFlags = atpXOvalue;
pb->ATPaddrBlock.aNet = addr->aNet;
pb->ATPaddrBlock.aNode = addr->aNode;
pb->ATPaddrBlock.aSocket= addr->aSocket;
/*** Set up the request BDS ***/
/*** first copy the data into the local buffer ***/
theSize = (theSize < ATPBSIZE )? theSize : ATPBSIZE;
BlockMove( theData, cp->buf, (long)theSize );
(cp->buf)[theSize] = '\0'; /*** request MUST BE null terminated ***/
pb->ATPreqLength = theSize;
pb->ATPreqPointer = cp->buf;
/*** set up the response BDS ***/
bdsPtr = cp->rsp;
for( i = 0; i < MAXBDS; i++ ){
cp->bds[i].buffPtr = bdsPtr;
cp->bds[i].buffSize = ATPBSIZE;
cp->bds[i].dataSize = 0;
cp->bds[i].userBytes = 0L;
bdsPtr += ATPBSIZE;
}
pb->ATPbdsPointer = cp->bds; /*** bds points to response ***/
pb->ATPnumOfBuffs = MAXBDS; /*** max num of response elements ***/
/*** Do the timeout parameters and issue the request... ***/
pb->ATPtimeOutVal = interval;
pb->ATPretryCount = count;
cp->isInUse = WAIT_RESPONSE;
error = PSendRequest( pb, ASYNC); /*** error will be picked up later ***/
if( error >= 0 )
error = noErr;
else /*** we fell down, so deallocate ***/
if( cp->rspM ){
HUnlock( cp->rspM );
DisposHandle( cp->rspM );
cp->rspM = nil;
}
return( error );
}
SendWaitingRequest( atp, cp )
ATPBlock *atp;
ClientPtr cp;
/*********************************
* Attempt to send a waiting request from
* the waiting queue using the transaction
* block whose index is passed.
*
* We step through the waiting queue until
* we find the next available element. Attempt
* to send it. If it sends ok, delete it from
* the queue, otherwise, keep it around for a
* whack at it later.
*
* In: i == index to available client
* transaction block
*
*********************************/
{
/* auto */ short error = noErr;
register ClientQH rq = atp->FirstRequest;
register ClientQPtr p = nil;
if( !rq )
return( NO_ERROR ); /*** nobody in the queue ***/
MoveHHi( rq );
HLock( rq );
p = *rq;
if( p->buf ){ /*** only send if we have data ***/
MoveHHi( p->buf );
HLock( p->buf );
error = SendRequest(cp,*(p->buf),(short)p->size,p->rsp,&(p->addr),p->count,p->interval);
HUnlock( p->buf );
}
HUnlock( rq );
if( !error )
DeQueueRequest( atp ); /*** got through, take the request away ***/
}
/*******************************************************************\
* Name Binding Protocol (NBP) Routines *
\*******************************************************************/
NBPBlock *NBPInit()
/*********************************
* Initialize the NBP side for the
* network.
*
* Returns a pointer to an NBP block
* if the open was successful, nil
* otherwise.
*
*********************************/
{
char *temp;
short i;
NBPBlock *nbp = nil;
/*** attempt to allocate the data ***/
if( !(nbp = (Ptr)NewPtr( sizeof( NBPBlock ))) )
return( nil );
/*** ------ Initialize the NBP side ------ ***/
nbp->NTEntry.nt.nteAddress.aSocket = 0;
nbp->NTEntry.nt.nteAddress.aNode = 0;
nbp->NTEntry.nt.nteAddress.aNet = 0;
temp = nbp->NTEntry.nt.entityData;
for( i = 0; i < 99; i++ ) /*** zero out the name string ***/
*temp++ = '\0';
nbp->LookUpBuffer = nil;
nbp->EntCount = 0;
nbp->Registered = false;
return( nbp );
}
short NBPKill( nbp )
NBPBlock *nbp;
/*********************************
* Disassociate this node with the network
* and release all memory used by NBP
*
* No nbp calls are made asynchronously
* so no need to shut any of them down.
*********************************/
{
/* auto */ OSErr error = noErr;
/* auto */ char *temp;
/* auto */ MPPParamBlock pb;
/*** Close Down and deallocate NBP side ***/
temp = nbp->NTEntry.nt.entityData;
if( *temp ){
pb.NBPentityPtr = nbp->NTEntry.nt.entityData;
error = PRemoveName( &pb, SYNC );
}
if( nbp->LookUpBuffer ){
HUnlock( nbp->LookUpBuffer );
DisposHandle( nbp->LookUpBuffer );
}
DisposPtr( nbp );
return( error );
}
short ConfirmName( nbp, name, addr, count, interval )
NBPBlock *nbp;
EntityName *name;
AddrBlock *addr;
short count, interval;
/*********************************
* Confirm that the name of a network
* visible entity is still visible
* to us.
*
* In: name = pointer to name
* in the form of a names table entry.
* addr = pointer to address
*
* Out: true if successful, socket set in addr
* false otherwise
*********************************/
{
/*auto */ OSErr error = noErr;
register MPPParamBlock Mpb;
Mpb.MPPioCompletion = nil; /*** as a matter of form ***/
Mpb.NBPentityPtr = name;
Mpb.NBPconfirmAddr.aNet = addr->aNet;
Mpb.NBPconfirmAddr.aNode = addr->aNode;
Mpb.NBPconfirmAddr.aSocket = addr->aSocket;
Mpb.NBPnewSocket = 0;
Mpb.NBPcount = count;
Mpb.NBPinterval = interval;
if( (error = PConfirmName( &Mpb, SYNC )) == noErr )
addr->aSocket = Mpb.NBPmaxToGet;
if( error )
return( false );
else
return( true );
}
short ExtractName( nbp, who, name, addr )
NBPBlock *nbp;
short who;
EntityName *name;
AddrBlock *addr;
/*********************************
* Extract a name from the socket
* listening table by index. Sets the
* send entity to this entity.
*
* In: who == Index to name to extract
* name == pointer to name result
* addr == pointer to addr result
* Out: 1 if successful
* 0 if not successful
* -1 if all entities searched
*
* Note that there is no "preferred" call
* for extract name. Because NBPExtract
* doesn't need a parameter block, no preferred
* call was needed.
*********************************/
{
/* auto */ short error;
/* auto */ short found = 0;
if( (nbp->EntCount <= 0) || (who > nbp->EntCount) )
return( -1 ); /*** no network visible entities ***/
MoveHHi( nbp->LookUpBuffer );
HLock( nbp->LookUpBuffer );
if( !(NBPExtract( *(nbp->LookUpBuffer),nbp->EntCount,who,name,addr)) )
found = 1;
HUnlock( nbp->LookUpBuffer );
return( found );
}
short Lookup( nbp, theType, theZone, num, count, interval )
NBPBlock *nbp;
char *theType;
char *theZone;
short num, count, interval;
/*********************************
* Lookup all network visible entities
* and fill the Lookup buffer accordingly
*
* In: (Handle) NBPBlock
* theType = type to lookup
* theZone = zone to lookup
* num = number to lookup
* count = (number of retries)
* interval= (X8 ticks)time between retries
*
* Out: number of elements returned
*********************************/
{
/* auto */ short error;
/* auto */ short i;
/* auto */ NamesTableEntry nte; /*** used internally by PLookupName ***/
/* auto */ long EntSize;
/* auto */ MPPParamBlock Mpb;
register char *np; /*** to fill out the names table ***/
/*** resize the record ***/
if( nbp->LookUpBuffer ){
HUnlock( nbp->LookUpBuffer );
DisposHandle( nbp->LookUpBuffer );
}
nbp->EntCount = 0;
EntSize = ( sizeof( NamesTableEntry) );
nbp->LookUpBuffer = NewHandle( (long)(num * EntSize) );
if( nbp->LookUpBuffer ){
MoveHHi( nbp->LookUpBuffer );
HLock( nbp->LookUpBuffer );
}
else
return( DEFAULT_ERROR ); /*** no room left for lookup buffer ***/
/*** Set the Search string to lookup everyone of requested type ***/
/*** the search string gets put away in a names table entry for ***/
/*** the preferred AppleTalk call ***/
/*** ----------------------------------------------------------- ***/
/*** The use of a names table entry as opposed to an EntityName is ***/
/*** a matter of form. Lookup wants to see the name in tuple form ***/
/*** (i.e. the entityname is packed). We borrow the entityData from ***/
/*** the namestableentry element (nte) and ignore the rest of the ***/
/*** structure. The use of a string[99] would have the same effect. ***/
nte.qNext = nil;
nte.nt.nteAddress.aNet = 0;
nte.nt.nteAddress.aNode = 0;
nte.nt.nteAddress.aSocket = 0;
np = nte.nt.entityData;
*np++ = '\1';
*np++ = '='; /*** all entities of this type please ***/
BlockMove( theType, np, (long)(*theType+1));
np += (*theType)+1;
BlockMove( theZone, np, (long)(*theZone+1) );
Mpb.NBPntQElPtr = nte.nt.entityData;
Mpb.NBPretBuffPtr = *(nbp->LookUpBuffer);
Mpb.NBPretBuffSize = (short)(EntSize * num);
Mpb.NBPmaxToGet = num;
Mpb.NBPinterval = interval;
Mpb.NBPcount = count;
if( !(error = PLookupName( &Mpb, SYNC)) )
nbp->EntCount = Mpb.NBPnumGotten;
HUnlock( nbp->LookUpBuffer );
return( nbp->EntCount );
}
short NodeRegister( nbp, nameOf, typeOf, count, interval, verify, addr )
NBPBlock *nbp;
char *typeOf;
char *nameOf;
short count;
short interval;
short verify;
AddrBlock *addr;
/*********************************
* Register this node as the entity
* passed as a parameter.
*
* In: (Ptr) typeof entity (provided by hyperTalk)
* (Ptr) nameof entity (from system resource)
* short retry interval
* short retry count
* short verify = name must be unique.
* Out: noErr if successful
* OSErr otherwise
*********************************/
{
/* auto */ short error;
register NamesTableEntry *nte = &(nbp->NTEntry);
register char *np; /*** pointer into names table ***/
/* auto */ MPPParamBlock Mpb;
/*** save off the entity in a packed names array ***/
np = nte->nt.entityData;
BlockMove( nameOf, np, (long)(*nameOf)+1 );
np += (*nameOf)+1;
BlockMove( typeOf, np, (long)(*typeOf)+1 );
np += (*typeOf)+1;
/*** According to Insdide AppleTalk, Zone Name must be '*' (this zone) ***/
*np++ = '\1';
*np++ = '*';
nte->nt.nteAddress.aSocket = addr->aSocket;
nte->nt.nteAddress.aNode = addr->aNode;
nte->nt.nteAddress.aNet = addr->aNet;
Mpb.NBPinterval = interval;
Mpb.NBPcount = count;
Mpb.NBPntQElPtr = nte;
Mpb.NBPverifyFlag = verify;
error = PRegisterName( &Mpb, SYNC);
if( !error )
nbp->Registered = true;
return( error );
}